{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "custom_layers.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.7.4"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "tDnwEv8FtJm7"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "JlknJBWQtKkI",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "60RdWsg1tETW"
      },
      "source": [
        "# カスタムレイヤー"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "BcJg7Enms86w"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/customization/custom_layers\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />View on TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ja/tutorials/customization/custom_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ja/tutorials/customization/custom_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/site/ja/tutorials/customization/custom_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Download notebook</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "YRXLphinx2fF"
      },
      "source": [
        "Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳は**ベストエフォート**であるため、この翻訳が正確であることや[英語の公式ドキュメント](https://www.tensorflow.org/?hl=en)の 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリ[tensorflow/docs](https://github.com/tensorflow/docs)にプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 [docs-ja@tensorflow.org メーリングリスト](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ja)にご連絡ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "UEu3q4jmpKVT"
      },
      "source": [
        "ニューラルネットワークの構築には、ハイレベルの API である `tf.keras` を使うことを推奨します。しかしながら、TensorFlow API のほとんどは、eager execution でも使用可能です。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "-sXDg19Q691F",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Py0m-N6VgQFJ",
        "colab": {}
      },
      "source": [
        "try:\n",
        "  # %tensorflow_version only exists in Colab.\n",
        "  %tensorflow_version 2.x\n",
        "except Exception:\n",
        "  pass\n",
        "import tensorflow as tf"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "zSFfVVjkrrsI"
      },
      "source": [
        "## レイヤー：有用な演算の共通セット\n",
        "\n",
        "機械学習モデルのコーディングでは、個々の演算やひとつひとつの変数のオペレーションよりは、より高度に抽象化されたオペレーションを行いたいのがほとんどだと思います。\n",
        "\n",
        "多くの機械学習モデルは、比較的単純なレイヤーの組み合わせや積み重ねによって表現可能です。TensorFlow では、多くの一般的なレイヤーのセットに加えて、アプリケーションに特有なレイヤーを最初から記述したり、既存のレイヤーの組み合わせによって作るための、簡単な方法が提供されています。\n",
        "\n",
        "TensorFlow には、tf.keras パッケージに[Keras](https://keras.io) APIのすべてが含まれています。Keras のレイヤーは、独自のモデルを構築する際に大変便利です。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "8PyXlPl-4TzQ",
        "colab": {}
      },
      "source": [
        "# tf.keras.layers パッケージの中では、レイヤーはオブジェクトです。\n",
        "# レイヤーを構築するためにすることは、単にオブジェクトを作成するだけです。\n",
        "# ほとんどのレイヤーでは、最初の引数が出力の次元あるいはチャネル数を表します。\n",
        "layer = tf.keras.layers.Dense(100)\n",
        "# 入力の次元数は多くの場合不要となっています。それは、レイヤーが最初に使われる際に\n",
        "# 推定可能だからです。ただし、引数として渡すことで手動で指定することも可能です。\n",
        "# これは複雑なモデルを構築する場合に役に立つでしょう。\n",
        "layer = tf.keras.layers.Dense(10, input_shape=(None, 5))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Fn69xxPO5Psr"
      },
      "source": [
        "既存のレイヤーのすべての一覧は、[ドキュメント](https://www.tensorflow.org/api_docs/python/tf/keras/layers)を参照してください。Dense（全結合レイヤー）、Conv2D、LSTM、BatchNormalization、Dropoutなどのたくさんのレイヤーが含まれています。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "E3XKNknP5Mhb",
        "colab": {}
      },
      "source": [
        "# レイヤーを使うには、単純にcallします。\n",
        "layer(tf.zeros([10, 5]))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Wt_Nsv-L5t2s",
        "colab": {}
      },
      "source": [
        "# レイヤーにはたくさんの便利なメソッドがあります。例えば、`layer.variables`を使って\n",
        "# レイヤーのすべての変数を調べることができます。訓練可能な変数は、 `layer.trainable_variables`\n",
        "# でわかります。この例では、全結合レイヤーには重みとバイアスの変数があります。\n",
        "layer.variables"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "6ilvKjz8_4MQ",
        "colab": {}
      },
      "source": [
        "# これらの変数には便利なアクセサを使ってアクセス可能です。\n",
        "layer.kernel, layer.bias"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "O0kDbE54-5VS"
      },
      "source": [
        "## カスタムレイヤーの実装\n",
        "\n",
        "独自のレイヤーを実装する最良の方法は、tf.keras.Layer クラスを拡張し、下記のメソッドを実装することです。\n",
        "  *  `__init__` , 入力に依存しないすべての初期化を行う\n",
        "  * `build`, 入力の `shape` を知った上で、残りの初期化を行う\n",
        "  * `call`, フォワード計算を行う\n",
        "\n",
        "`build` が呼ばれるまで変数の生成を待つ必要はなく、`__init__` で作成できることに注意してください。しかしながら、`build` で変数を生成することの優位な点は、レイヤーがオペレーションをしようとする入力の `shape` に基づいて、後から定義できる点です。これに対して、`__init__` で変数を生成するということは、そのために必要な `shape` を明示的に指定する必要があるということです。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "5Byl3n1k5kIy",
        "colab": {}
      },
      "source": [
        "class MyDenseLayer(tf.keras.layers.Layer):\n",
        "  def __init__(self, num_outputs):\n",
        "    super(MyDenseLayer, self).__init__()\n",
        "    self.num_outputs = num_outputs\n",
        "    \n",
        "  def build(self, input_shape):\n",
        "    self.kernel = self.add_variable(\"kernel\", \n",
        "                                    shape=[int(input_shape[-1]), \n",
        "                                           self.num_outputs])\n",
        "    \n",
        "  def call(self, input):\n",
        "    return tf.matmul(input, self.kernel)\n",
        "  \n",
        "layer = MyDenseLayer(10)\n",
        "print(layer(tf.zeros([10, 5])))\n",
        "print(layer.trainable_variables)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "tk8E2vY0-z4Z"
      },
      "source": [
        "できるだけ標準のレイヤーを使ったほうが、概してコードは読みやすく保守しやすくなります。コードを読む人は標準的なレイヤーの振る舞いに慣れているからです。`tf.keras.layers` にはないレイヤーを使いたい場合には、[githubのイシュー](http://github.com/tensorflow/tensorflow/issues/new)を登録するか、もっとよいのはプルリクエストを送ることです。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Qhg4KlbKrs3G"
      },
      "source": [
        "## モデル：レイヤーの組み合わせ\n",
        "\n",
        "機械学習では、多くのレイヤーに類するものが、既存のレイヤーを組み合わせることで実装されています。例えば、ResNetの残差ブロックは、畳込み、バッチ正規化とショートカットの組み合わせです。\n",
        "\n",
        "他のレイヤーからなるレイヤーに類するものを定義する際の主役は、tf.keras.Model クラスです。このクラスを継承することで実装できます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "N30DTXiRASlb",
        "colab": {}
      },
      "source": [
        "class ResnetIdentityBlock(tf.keras.Model):\n",
        "  def __init__(self, kernel_size, filters):\n",
        "    super(ResnetIdentityBlock, self).__init__(name='')\n",
        "    filters1, filters2, filters3 = filters\n",
        "\n",
        "    self.conv2a = tf.keras.layers.Conv2D(filters1, (1, 1))\n",
        "    self.bn2a = tf.keras.layers.BatchNormalization()\n",
        "\n",
        "    self.conv2b = tf.keras.layers.Conv2D(filters2, kernel_size, padding='same')\n",
        "    self.bn2b = tf.keras.layers.BatchNormalization()\n",
        "\n",
        "    self.conv2c = tf.keras.layers.Conv2D(filters3, (1, 1))\n",
        "    self.bn2c = tf.keras.layers.BatchNormalization()\n",
        "\n",
        "  def call(self, input_tensor, training=False):\n",
        "    x = self.conv2a(input_tensor)\n",
        "    x = self.bn2a(x, training=training)\n",
        "    x = tf.nn.relu(x)\n",
        "\n",
        "    x = self.conv2b(x)\n",
        "    x = self.bn2b(x, training=training)\n",
        "    x = tf.nn.relu(x)\n",
        "\n",
        "    x = self.conv2c(x)\n",
        "    x = self.bn2c(x, training=training)\n",
        "\n",
        "    x += input_tensor\n",
        "    return tf.nn.relu(x)\n",
        "\n",
        "    \n",
        "block = ResnetIdentityBlock(1, [1, 2, 3])\n",
        "print(block(tf.zeros([1, 2, 3, 3])))\n",
        "print([x.name for x in block.trainable_variables])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "wYfucVw65PMj"
      },
      "source": [
        "しかし、ほとんどの場合には、モデルはレイヤーを次々に呼び出すことで構成されます。tf.keras.Sequential クラスを使うことで、これをかなり短いコードで実装できます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "L9frk7Ur4uvJ",
        "colab": {}
      },
      "source": [
        "my_seq = tf.keras.Sequential([tf.keras.layers.Conv2D(1, (1, 1), \n",
        "                                                    input_shape=(\n",
        "                                                        None, None, 3)),\n",
        "                             tf.keras.layers.BatchNormalization(),\n",
        "                             tf.keras.layers.Conv2D(2, 1,\n",
        "                                                    padding='same'),\n",
        "                             tf.keras.layers.BatchNormalization(),\n",
        "                             tf.keras.layers.Conv2D(3, (1, 1)),\n",
        "                             tf.keras.layers.BatchNormalization()])\n",
        "my_seq(tf.zeros([1, 2, 3, 3]))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "c5YwYcnuK-wc"
      },
      "source": [
        "# 次のステップ\n",
        "\n",
        "それでは、前出のノートブックに戻り、線形回帰の例を、レイヤーとモデルを使って、より構造化された形で実装してみてください。"
      ]
    }
  ]
}